#include "teex/base64.h"

static const char *base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
static const unsigned char base64Map[256] = {
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0x00-0x0F
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0x10-0x1F
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,    // 0x20-0x2F
	 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255,  64, 255, 255,    // 0x30-0x3F
	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,    // 0x40-0x4F
	 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,    // 0x50-0x5F
	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,    // 0x60-0x6F
	 41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,    // 0x70-0x7F
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0x80-0x8F
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0x90-0x9F
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0xA0-0xAF
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0xB0-0xBF
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0xC0-0xCF
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0xD0-0xDF
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,    // 0xE0-0xEF
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255    // 0xF0-0xFF
};

int base64_encode(const void *data, int size, char *code) {
	int nDiv = size / 3;
	int nMod = size % 3;
	int i;
	const unsigned char *d = (const unsigned char *)data;
	unsigned int d0, d1, d2, dd;
	char *p = code;
	for(i = 0; i < nDiv; i++) {
		d0 = *(d++);
		d1 = *(d++);
		d2 = *(d++);
		dd = (d0<<16)|(d1<<8)|(d2);
		*(p++) = base64Table[(dd>>18) & 0x3F];
		*(p++) = base64Table[(dd>>12) & 0x3F];
		*(p++) = base64Table[(dd>>6) & 0x3F];
		*(p++) = base64Table[dd & 0x3F];
	}
	if(nMod == 2) {
		unsigned char d0 = *(d++);
		unsigned char d1 = *d;
		unsigned char d2 = 0;
		*(p++) = base64Table[(d0>>2) & 0x3F];
		*(p++) = base64Table[((d0<<4)|(d1>>4)) & 0x3F];
		*(p++) = base64Table[((d1<<2)|(d2>>6)) & 0x3F];
		*(p++) = '=';
	}
	else if(nMod == 1) {
		unsigned char d0 = *d;
		unsigned char d1 = 0;
		//unsigned char d2 = 0;
		*(p++) = base64Table[(d0>>2) & 0x3F];
		*(p++) = base64Table[((d0<<4)|(d1>>4)) & 0x3F];
		*(p++) = '=';
		*(p++) = '=';
	}
	*p = '\0';
	return p - (char *)code;
}

int base64_decode(const char *code, void *data) {
	int len = 0;
	const char *c = code;
	while(*(c++) != '\0') {
		len++;
	}
	if(len % 4 != 0) {
		return -1;
	}
	if(len == 0) {
		return 0;
	}
	c = code;
	int nDiv = len / 4;
	unsigned int c0, c1, c2, c3, cc;
	unsigned char *p = (unsigned char *)data;
	int i;
	for(i = 0; i+1 < nDiv; ++i) {
		c0 = base64Map[(int)*(c++)];
		c1 = base64Map[(int)*(c++)];
		c2 = base64Map[(int)*(c++)];
		c3 = base64Map[(int)*(c++)];
		if(c0>=64 || c1>=64 || c2>=64 || c3>=64) {
			return -1;
		}
		cc = (c0<<18)|(c1<<12)|(c2<<6)|(c3);
		*(p++) = (cc>>16) & 0xFF;
		*(p++) = (cc>>8) & 0xFF;
		*(p++) = (cc) & 0xFF;
	}
	c0 = base64Map[(int)*(c++)];
	c1 = base64Map[(int)*(c++)];
	c2 = base64Map[(int)*(c++)];
	c3 = base64Map[(int)*(c++)];
	if(c0>=64 || c1>=64 || c2>64 || c3>64) {
		return -1;
	}
	cc = (c0<<18)|(c1<<12);
	*(p++) = (cc>>16) & 0xFF;
	if(c2<=63) {
		cc |= (c2<<6);
		*(p++) = (cc>>8) & 0xFF;
		if(c3 != 64) {
			cc |= (c3);
			*(p++) = (cc) & 0xFF;
		}
		else {
			if((c2 & 0x03) != 0) {
				return -1;
			}
		}
	}
	else {
		if(c3 != 64 || (c1 & 0x0F) != 0) {
			return -1;
		}
	}
	return p - (unsigned char *)data;
}

